#include <assert.h>
#include <errno.h>
#include <signal.h>
#include "time.h"
#include <stdlib.h>
#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>
#include "threadStructure.h"
#include "thread.h"
#include "util.h"
#include "timer.h"


static const int SIGNAL_TYPE = SIGALRM;
/*the quantum in usec */
static const long int QUANTUM = 1000;


static void setAlarm();
static void setSet(sigset_t *setp);

void
interruptsOn()
{
  int ret;
  sigset_t sigset;

  setSet(&sigset);
  ret = sigprocmask(SIG_UNBLOCK, &sigset, NULL);
  assert(!ret);
}



void
interruptsOff()
{
  int ret;

  sigset_t sigset;

  setSet(&sigset);
  ret = sigprocmask(SIG_BLOCK, &sigset, NULL);
  assert(!ret);
}


static void setSet(sigset_t *setp){
  int ret;
  ret = sigemptyset(setp);
  assert(!ret);
  ret = sigpending(setp);
  assert(!ret);
  return;
}


/*
 * Should be called when you initialize threads package.
 */
void scheduler(){
  struct sigaction action;
  int error;

  action.sa_handler = NULL;
  action.sa_sigaction = (void(*)(int, siginfo_t *, void *))interruptHandler;
  /* SIGNAL_TYPE will be blocked by default
   * while handler invoked. No mask needed */
  error = sigemptyset(&action.sa_mask);
  assert(!error);
  /* use sa_sigaction as handler
   * instead of sa_handler*/
  action.sa_flags = SA_SIGINFO; 

  if(sigaction(SIGNAL_TYPE, &action, NULL)){
    perror("Setting up signal handler");
    assert(0);
  }

  setAlarm();
}

/*
 * STUB: once scheduler is called, this routine
 * gets called each time SIGNAL_TYPE is sent to this process
 */
void interruptHandler(void){
  /*
   * SIGALRM is masked on entry
   */
  sigset_t mask;
  int ret = sigprocmask(0, NULL, &mask);
  if(ret)
    perror("sigprocmask"), exit(-1);
  assert(sigismember(&mask, SIGALRM));
  setAlarm();
  thread_yield();
  return;
}

static void setAlarm(){
  ualarm(QUANTUM, 0);
}


int
alarmIsEnabled()
{
  sigset_t mask;
  int ret;

  ret = sigprocmask(0, NULL, &mask);
  assert(!ret);
  return(sigismember(&mask, SIGALRM) ? 0 : 1);
}
